﻿using System;
using System.Collections.Generic;
using IrrlichtLime;
using IrrlichtLime.Core;
using Timed;
using IrrlichtLime.Scene;
using IrrlichtLime.Video;

namespace Iso
{
    public class AnimatedSprite
    {
        public string name;
        public int sheetWidth, sheetHeight;
        public uint timePerFrame;
        public Dictionary<string, SpriteAnimationInfo> animations;
        public bool isFinished;
        public MeshSceneNode sceneNode;
        public bool animated;
        public bool facingCamera;

        SpriteAnimationInfo currentAnimation;
        Vector2Df zero;
        uint startTime, lastI;

        public AnimatedSprite(string name, Dimension2Df size, Vector3Df position, float rotation, bool facingCamera)
        {
            this.name = name;
            this.facingCamera = facingCamera;
            LoadInfoTXT("data\\spritesheets\\" + name + ".txt");

            float halfWidth = size.Width / 2;
            MeshBuffer buff = MeshBuffer.Create(VertexType.Standard, IndexType._32Bit);
            buff.Append(new Vertex3D[]{
                new Vertex3D(new Vector3Df(-halfWidth, 0, 0), new Vector3Df(0, 0, 0), Color.OpaqueWhite, new Vector2Df(0,1)),
                new Vertex3D(new Vector3Df(halfWidth, 0, 0), new Vector3Df(0, 0, 0), Color.OpaqueWhite, new Vector2Df(1,1)),
                new Vertex3D(new Vector3Df(-halfWidth, size.Height, 0), new Vector3Df(0, 0, 0), Color.OpaqueWhite, new Vector2Df(0,0)),
                new Vertex3D(new Vector3Df(halfWidth, size.Height, 0), new Vector3Df(0, 0, 0), Color.OpaqueWhite, new Vector2Df(1,0))},
                new uint[] { 1, 0, 2, 1, 2, 3 });
            buff.RecalculateBoundingBox();
            Mesh mesh = Mesh.Create();
            mesh.AddMeshBuffer(buff);
            buff.Drop();
            mesh.RecalculateBoundingBox();

            sceneNode = Engine.device.SceneManager.AddMeshSceneNode(mesh, null, -1, position);
            sceneNode.SetMaterialTexture(0, Engine.device.VideoDriver.GetTexture("data/spritesheets/" + name + ".png"));
            sceneNode.SetMaterialFlag(MaterialFlag.Lighting, false);
            sceneNode.SetMaterialType(MaterialType.TransparentAlphaChannelRef);
            sceneNode.SetMaterialFlag(MaterialFlag.BilinearFilter, false);
            sceneNode.SetMaterialFlag(MaterialFlag.BackFaceCulling, false);
            sceneNode.Rotation = new Vector3Df(0, rotation, 0);
            sceneNode.AutomaticCulling = CullingType.Off;

            zero = new Vector2Df(0, 0);
            isFinished = false;
        }

        private void LoadInfoTXT(string filename)
        {
            if (System.IO.File.Exists(filename))
            {
                string[] lines = filename.GetFileLines();
                this.timePerFrame = uint.Parse(lines[0]);
                this.sheetWidth = int.Parse(lines[1]);
                this.sheetHeight = int.Parse(lines[2]);

                animations = new Dictionary<string, SpriteAnimationInfo>();
                for (int i = 3; i < lines.Length; i++)
                {
                    string[] s = lines[i].Split(' ');
                    string name = s[0];
                    int begin = int.Parse(s[1]);
                    int end = int.Parse(s[2]);
                    bool loop = bool.Parse(s[3]);

                    animations.Add(name, new SpriteAnimationInfo(begin, end, loop));
                }

                animated = true;
            }
            else
                animated = false;
        }

        public void SetAnimation(string name)
        {
            isFinished = false;
            currentAnimation = animations[name];
            startTime = Engine.device.Timer.RealTime;
        }

        public void Update()
        {
            if (facingCamera)
            {
                Vector3Df vect = Engine.camera.Position - sceneNode.Position;
                vect = vect.HorizontalAngle;
                vect.Z = 0;
                vect.X = 0;
                sceneNode.Rotation = vect;
            }

            if (animated)
            {
                if (currentAnimation != null && !(!currentAnimation.loop && !isFinished))
                {
                    uint t = Engine.device.Timer.RealTime - startTime;
                    uint i = t / timePerFrame + (uint)currentAnimation.begin;
                    lastI = i;

                    if (i > currentAnimation.end)
                    {
                        if (currentAnimation.loop)
                        {
                            i = (uint)currentAnimation.begin;
                            isFinished = true;
                            startTime = Engine.device.Timer.RealTime;
                        }
                        else
                            isFinished = true;
                    }
                    else if (!currentAnimation.loop)
                        isFinished = false;

                    float x = i % sheetWidth, y = i / sheetWidth;
                    sceneNode.GetMaterial(0).SetTextureMatrix(0, Matrix.Identity.BuildTextureTransform(0,
                        zero, new Vector2Df(x / sheetWidth, y / sheetHeight), new Vector2Df(1f / sheetWidth, 1f / sheetHeight)));
                }
                else
                {
                    float x = lastI % sheetWidth, y = lastI / sheetWidth;
                    sceneNode.GetMaterial(0).SetTextureMatrix(0, Matrix.Identity.BuildTextureTransform(0,
                        zero, new Vector2Df(x / sheetWidth, y / sheetHeight), new Vector2Df(1f / sheetWidth, 1f / sheetHeight)));
                }
            }
        }

        public void Remove()
        {
            sceneNode.Remove();
        }
    }

    public class AnimatedSprite2D
    {
        public string name;
        public int sheetWidth, sheetHeight;
        public uint timePerFrame;
        public Dictionary<string, SpriteAnimationInfo> animations;
        public bool isFinished;
        public bool animated;
        public Recti rect;

        SpriteAnimationInfo currentAnimation;
        uint startTime, lastI;
        Texture tex;
        int width;
        int height;

        public AnimatedSprite2D(string name, Recti rect)
        {
            this.name = name;
            this.rect = rect;
            tex = Engine.device.VideoDriver.GetTexture("data/spritesheets/" + name + ".png");
            LoadInfoTXT("data\\spritesheets\\" + name + ".txt");
            isFinished = false;
        }

        private void LoadInfoTXT(string filename)
        {
            if (System.IO.File.Exists(filename))
            {
                string[] lines = filename.GetFileLines();
                this.timePerFrame = uint.Parse(lines[0]);
                this.sheetWidth = int.Parse(lines[1]);
                this.sheetHeight = int.Parse(lines[2]);

                animations = new Dictionary<string, SpriteAnimationInfo>();
                for (int i = 3; i < lines.Length; i++)
                {
                    string[] s = lines[i].Split(' ');
                    string name = s[0];
                    int begin = int.Parse(s[1]);
                    int end = int.Parse(s[2]);
                    bool loop = bool.Parse(s[3]);

                    animations.Add(name, new SpriteAnimationInfo(begin, end, loop));
                }

                animated = true;
                width = tex.Size.Width / sheetWidth;
                height = tex.Size.Height / sheetHeight;
            }
            else
                animated = false;
        }

        public void SetAnimation(string name)
        {
            isFinished = false;
            currentAnimation = animations[name];
            startTime = Engine.device.Timer.RealTime;
        }

        public void Draw()
        {
            if (animated)
            {
                if (currentAnimation != null && !(!currentAnimation.loop && !isFinished))
                {
                    uint t = Engine.device.Timer.RealTime - startTime;
                    uint i = t / timePerFrame + (uint)currentAnimation.begin;
                    lastI = i;

                    if (i > currentAnimation.end)
                    {
                        if (currentAnimation.loop)
                        {
                            i = (uint)currentAnimation.begin;
                            isFinished = true;
                            startTime = Engine.device.Timer.RealTime;
                        }
                        else
                            isFinished = true;
                    }
                    else if (!currentAnimation.loop)
                        isFinished = false;

                    int x = (int)(i % sheetWidth) * width, y = (int)(i / sheetWidth) * height;
                    Engine.device.VideoDriver.Draw2DImage(tex, rect, new Recti(x, y, x + width, y + height), null, null, true);
                }
                else
                {
                    int x = (int)(lastI % sheetWidth) * width, y = (int)(lastI / sheetWidth) * height;
                    Engine.device.VideoDriver.Draw2DImage(tex,
                        rect, new Recti(x, y, x + width, y + height), null, null, true);
                }
            }
            else
                Engine.device.VideoDriver.Draw2DImage(tex, rect, new Recti(0, 0, tex.Size.Width, tex.Size.Height), null, null, true);
        }

        public void Remove()
        {
            Engine.device.VideoDriver.RemoveTexture(Engine.device.VideoDriver.GetTexture("data/spritesheets/" + name + ".png"));
        }
    }

    public class SpriteAnimationInfo
    {
        public int begin, end;
        public bool loop;

        public SpriteAnimationInfo(int begin, int end, bool loop)
        {
            this.begin = begin;
            this.end = end;
            this.loop = loop;
        }
    }
}
